﻿using Abp.Domain.Repositories;
using Abp.UI;
using App.Authorization.Roles;
using App.Authorization.Users.Dto;
using App.BaseObject;
using App.Common;
using App.Configuration;
using App.Organizations;
using EntityFramework.Extensions;
using Omu.ValueInjecter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using App.MultiTenancy;

namespace App.Authorization.Users
{
    public class UserAppService : AppServiceBase, IUserAppService
    {
        private readonly UserManager _userManager;
        private readonly IRepository<OrganizationUnit> _teamRepository;
        private readonly IRepository<Role> _roleRepository;
        private readonly IRepository<LoginLog> _loginLogRepository;
        private readonly IRepository<CalendarEvent> _calendarRepository;
        private readonly IRepository<UserPermission> _userPermissionRepository;

        public UserAppService(
            UserManager userManager,
            IRepository<OrganizationUnit> teamRepository,
            IRepository<Role> roleRepository,
            IRepository<LoginLog> loginLogRepository,
            IRepository<UserPermission> userPermissionRepository,
            IRepository<CalendarEvent> calendarRepository)
        {
            _userManager = userManager;
            _teamRepository = teamRepository;
            _roleRepository = roleRepository;
            _loginLogRepository = loginLogRepository;
            _userPermissionRepository = userPermissionRepository;
            _calendarRepository = calendarRepository;
        }

        public async Task<User> CreateUser(UserDto dto)
        {
            if (dto.Password != dto.ConfrimPassword)
            {
                throw new UserFriendlyException("两次输入的密码不一致！");
            }

            if (await _userManager.CountAsync(u => u.UserName == dto.UserName) > 0)
            {
                throw new UserFriendlyException(string.Format("用户名为【{0}】的用户已经存在！", dto.UserName));
            }

            var user = new User();
            user.InjectFrom(dto);
            user.TenantId = AbpSession.TenantId;
            user.PasswordHash = PasswordHash.CreateHash(dto.Password);
            user.OrganizationUnit = await _teamRepository.GetAsync(dto.TeamId);
            user.Roles = new List<UserRole>();
            foreach (var roleId in dto.RoleId.Where(roleId => roleId != 0))
            {
                user.Roles.Add(new UserRole { RoleId = roleId });
            }

            return await _userManager.SaveOrUpdateUserAsync(user);
        }

        public async Task UpdateUser(long id, UserDto dto)
        {
            var user = await _userManager.GetById(id);
            user.InjectFrom(dto);
            if (!string.IsNullOrEmpty(dto.Password))
                user.PasswordHash = PasswordHash.CreateHash(dto.Password);
            user.OrganizationUnit = await _teamRepository.GetAsync(dto.TeamId);

            await _userManager.SaveOrUpdateUserAsync(user);
            await _userManager.SetRoles(user, dto.RoleId.ToArray());
        }

        public async Task ChangeUserPassword(ChangePwdDto dto)
        {
            var user = await _userManager.GetById(AbpSession.UserId.Value);
            if (user == null || !PasswordHash.ValidatePassword(dto.OldPassword, user.PasswordHash))
            {
                throw new UserFriendlyException("输入的旧密码与原来的密码不一致，密码修改失败！");
            }

            user.PasswordHash = PasswordHash.CreateHash(dto.Password);

            await _userManager.SaveOrUpdateUserAsync(user);
        }

        public async Task<User> GetUserByUserNameAsync(string userName)
        {
            return await _userManager.GetByUserName(userName);
        }

        public async Task<User> GetUserById(long userId)
        {
            var user = await _userManager.GetById(userId);
            return user;
        }

        public async Task<List<LoginLog>> GetUserLoginLogs(long userId)
        {
            return await _loginLogRepository.GetAllListAsync(s => s.UserId == userId);
        }

        public async Task SaveUserButtons(int id, BulkKeyDto<int> dto)
        {
            var allPermissions = await _userManager.GetAllMenuByUserId(id);
            var permissions = allPermissions.Select(s => s.Id).ToArray();
            await _userPermissionRepository.GetAll().Where(s => s.UserId == id && !dto.KeyList.Contains(s.MenuId)).DeleteAsync();

            foreach (var menuId in dto.KeyList)
            {
                if (menuId != 0 && !permissions.Contains(menuId)) await _userPermissionRepository.InsertAsync(new UserPermission { MenuId = menuId, UserId = id });
            }
        }

        public async Task<List<MenuPermission>> GetAllMenuByUserId(long userId)
        {
            return await _userManager.GetAllMenuByUserId(userId);
        }

        public async Task DeleteAsync(long[] ids)
        {
            foreach (var id in ids)
            {
                await _userManager.DeleteAsync(id);
            }
        }

        public async Task<long> GetLoginCount(long userId)
        {
            return await _loginLogRepository.CountAsync(s => s.UserId == userId);
        }

        public async Task<List<CalendarEvent>> GetCalendarEvents(DateTime start, DateTime end)
        {
            return await _calendarRepository.GetAllListAsync(c => c.User.Id == AbpSession.UserId && c.StartDate >= start && c.EndDate <= end);
        }

        public async Task UpdateCalendarEvent(int id, CalendarEventDto dto)
        {
            var calendarEvent = await _calendarRepository.GetAsync(id);

            calendarEvent.InjectFrom(dto);
            calendarEvent.Subject = dto.Title;
        }

        public async Task CreateCalendarEvent(CalendarEventDto dto)
        {
            var calendarEvent = new CalendarEvent();
            calendarEvent.InjectFrom(dto);
            calendarEvent.TenantId = AbpSession.TenantId;
            calendarEvent.User = await _userManager.GetById(AbpSession.UserId.Value);
            calendarEvent.Subject = dto.Title;

            await _calendarRepository.InsertAsync(calendarEvent);
        }

        public async Task DeleteCalendarEventAsync(int id)
        {
            await _calendarRepository.GetAll().Where(s => s.Id == id).DeleteAsync();
        }

        public Task<Tenant> GetTenantByUserId(long userId)
        {
            throw new NotImplementedException();
        }
    }
}
